#pragma once
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdarg.h>
#include <sys/mman.h>
#include <sys/uio.h>

//请求方法 
enum METHOD {GET = 0, POST};
//请求状态
enum HTTP_CODE {NO_REQUEST = 0, GET_REQUEST, BAD_REQUEST, FORBIDDEN_REQUEST, 
                INTERNAL_ERROR, NO_RESOURCE, FILE_REQUEST, CLOSED_CONNECTION};
//行状态
enum LINE_STATE {LINE_OK = 0, LINE_OPEN, LINE_BAD};
//主状态机
enum CHECK_STATE {REQUEST_LINE = 0, REQUEST_HEADER, REQUEST_CONTENT};

extern int setNonBlock(int fd);
extern void addEvent(int epfd, int sock, bool epolloneshot);
extern void modEvent(int epfd, int sock, int ev);
extern void delEvent(int epfd, int sock);

class http_conn
{
public:
    http_conn()
    {}

    ~http_conn()
    {}
public:
    //初始化新的连接
    void init(struct sockaddr_in& address, int sockfd);
    //关闭连接
    void close_conn(bool real_close = true);
    //处理客户端请求
    void process();
    //非阻塞读
    bool read_();
    //非阻塞写
    bool write_();
private:
    //初始化成员
    void init();
    HTTP_CODE process_read();
    bool process_write(HTTP_CODE ret);

    //处理http请求
    LINE_STATE parse_line();
    HTTP_CODE parse_request_line(char* text);
    HTTP_CODE parse_request_header(char* text);
    HTTP_CODE parse_content(char* text);
    HTTP_CODE do_request();

    char* get_line()
    {
        return m_rd_buffer + m_start_line;
    }

    //填充http应答
    void unmap();
    bool add_response_status(int status_code, const char* title);
    bool add_response_header(int content_len);
    bool add_response_blank();
    bool add_response_content(const char* content);
    bool add_content_length(int content_len);
    bool add_linger();
    bool add_response(const char* format, ...);
public:
    //epoll模型标识符
    static int m_epfd;
    //用户连接数量
    static int m_user_cnt;
    //读缓冲区大小
    static const int RD_BUFFER_SIZE = 2048;
    //写缓冲区大小
    static const int WR_BUFFER_SIZE = 1024;
    //文件名最大长度
    static const int FILE_NAME_SIZE = 200;  
private:
    //客户端地址
    struct sockaddr_in m_client;
    //文件描述符
    int m_confd;

    //读缓冲区
    char m_rd_buffer[RD_BUFFER_SIZE];
    //读缓冲区指针
    int m_read_x;
    //实际读数据指针
    int m_check_x;
    //行长度
    int m_start_line;

    //写缓冲区
    char m_wr_buffer[WR_BUFFER_SIZE];
    //写缓冲区指针
    int m_write_x;
    
    //检查状态
    CHECK_STATE m_check_state;
    //请求方法
    METHOD m_method;
    //客户请求的目标文件的完整路径
    char m_file_path[FILE_NAME_SIZE];
    //请求url
    char* m_url;
    //请求版本
    char* m_version;
    //主机名
    char* m_host;
    //正文内容长度
    int m_content_length;
    //是否要保持长连接
    bool m_keep_live;

    //客户请求的目标文件被mmap到内存的起始为止
    char* m_file_address;
    //文件状态
    struct stat m_file_state;
    //采用writev来写数据
    struct iovec m_iov[2];
    int m_iov_cnt;
};